package com.zhang.mongodb.service.impl;

import com.mongodb.client.MongoCollection;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSBuckets;
import com.mongodb.client.gridfs.GridFSFindIterable;
import com.mongodb.client.gridfs.model.GridFSFile;
import com.mongodb.client.gridfs.model.GridFSUploadOptions;
import com.mongodb.client.model.Filters;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import com.zhang.mongodb.common.model.UserDO;
import com.zhang.mongodb.dao.UserRepository;
import com.zhang.mongodb.service.ITestService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.gridfs.GridFsCriteria;
import org.springframework.data.mongodb.gridfs.GridFsResource;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;

/**
 * description
 *
 * @author zhangFanJun
 * @date 2024-03-29 17:49
 **/
@RequiredArgsConstructor
@Slf4j
@Service
public class TestService implements ITestService {

    private final UserRepository userRepository;
    private final MongoTemplate mongoTemplate;
    private final GridFsTemplate gridFsTemplate;

    @Override
    public String get() throws IOException {

        crud();
        find();
        gridfs();
        return "访问成功";
    }


    public void crud() {

        UserDO one = getUserDO();
        UserDO save = userRepository.save(one);
        UserDO UserDO = userRepository.findById(save.getId()).orElse(null);
        if (Objects.nonNull(UserDO)) {
            log.info("查找保存的数据UserDO:{}", UserDO);
            userRepository.deleteById(save.getId());
            UserDO again = userRepository.findById(save.getId()).orElse(null);
            if (Objects.isNull(again)) {
                log.info("删除后无法查找到数据");
            } else {
                log.info("删除后查找到数据：{}", again);
            }
        } else {
            log.info("无法查找到保存的数据");
        }

        userRepository.save(one);
    }

    private UserDO getUserDO() {
        UserDO one = new UserDO();
        one.setId(UUID.randomUUID().toString());
        one.setUserId(5566L + ThreadLocalRandom.current().nextInt(99));
        one.setUsername("张三");
        one.setDescription("张三是个好人" + DateFormatUtils.format(new Date(), "yyyy/MM/dd HH:mm:ss"));
        one.setCreateTime(new Date());
        List<UserDO> friends = new ArrayList<>();

        UserDO f = new UserDO();
        f.setId(UUID.randomUUID().toString());
        f.setUserId(5566L + ThreadLocalRandom.current().nextInt(99));
        f.setUsername("李四");
        f.setDescription("张三是个好人" + DateFormatUtils.format(new Date(), "yyyy/MM/dd HH:mm:ss"));
        f.setCreateTime(new Date());
        friends.add(f);
        one.setFriendList(friends);
        return one;
    }

    public void find() {
        UserDO one = getUserDO();
        UserDO insert = mongoTemplate.insert(one);
        log.info("template-insert ==》新增对象：{}", insert);

        String username = one.getUsername() + "-改名字-" + UUID.randomUUID().toString();
        one.setUsername(username);
        UserDO save = mongoTemplate.save(one);
        log.info("template-save ==》保存或者修改对象：{}", save);

        Query query = Query.query(Criteria.where("username").is(username));
        UserDO findOne = mongoTemplate.findOne(query, UserDO.class, "users");
        log.info("template-findOne ==》{}", findOne);

        Update update = Update.update("description", "查到数据并做了一次修改");
        UserDO findAndModify = mongoTemplate.findAndModify(query, update, UserDO.class, "users");
        log.info("template-findAndModify ==》{}", findAndModify);

        update.set("description", "存在则做修改");
        UpdateResult upsert = mongoTemplate.upsert(query, update, UserDO.class, "users");
        log.info("template-upsert 存在则修改，不存在就新增==》{}", upsert);


        update.set("description", "更新查询到的第一个");
        UpdateResult updateFirst = mongoTemplate.updateFirst(query, update, UserDO.class, "users");
        log.info("template-updateFirst 更新查询到的第一个==》{}", updateFirst);

        DeleteResult remove = mongoTemplate.remove(query, UserDO.class, "users");
        log.info("template-remove 删除==》{}", remove);

        String collectionName = mongoTemplate.getCollectionName(UserDO.class);
        log.info("template-collectionName 集合名字==》{}", collectionName);

        MongoCollection<Document> getCollection = mongoTemplate.getCollection("users");
        log.info("template-getCollection 查询该集合中的所有文档 ==》{}", getCollection);


        // 构建聚合操作
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(Criteria.where("username").is("张三")),
                Aggregation.group("createTime").count().as("total")
        );

        // 执行聚合操作并返回结果
        AggregationResults<UserDO> results = mongoTemplate.aggregate(aggregation, "users", UserDO.class);
        List<UserDO> userList = results.getMappedResults();
        log.info("查询结果：{}", userList);
    }


    /**
     * gridFsTemplate无法设置桶有关的数据，封装比较优雅，
     * GridFSBuckets能实现比较灵活的操作，包括桶相关的设置
     * */
    private void gridfs() throws IOException {

        String filePath = "E:\\appData";
        File file = new File(filePath + "\\凤凰架构.pdf");
        Document document = new Document();
        document.append("fileName", file.getName());
        document.append("from", "local");

        // 测试发现gridFsTemplate是并没有桶相关的设置，只能存在metadata，store的时候初始化了GridFsUpload
        GridFSUploadOptions options = new GridFSUploadOptions()
                // 块大小 1M
                .chunkSizeBytes(1024 * 1024)
                .metadata(document);

        // 存储的时候可以存放文件流，文件名，文件类型，文档
        ObjectId objectId = gridFsTemplate.store(new FileInputStream(file), file.getName(), document);
        String hexString = objectId.toHexString();
        log.info("上传成功：{}", objectId);
        log.info("hexString：{}", hexString);

        // 查看底层会发现其实是findOne方法
        GridFsResource resource = gridFsTemplate.getResource("凤凰架构.pdf");
        if (resource.exists()) {
            IOUtils.copy(resource.getInputStream(), new FileOutputStream(new File(filePath + File.separator + UUID.randomUUID().toString() + resource.getFilename())));
            log.info("下载结束");
        }

        // 查看源码，发现名字携带了*或者是？才会执行find查找，否则等同于getResource
        GridFsResource[] resources = gridFsTemplate.getResources("凤凰架构.pdf");
        for (GridFsResource gridFsResource : resources) {
            log.info("查找到的资源fileId：{}", gridFsResource.getFileId());
        }

        // 查看getResource发现这个方法
//        Query query = Query.query(GridFsCriteria.whereFilename().is("凤凰架构.pdf"));
        Query query = Query.query(GridFsCriteria.whereMetaData("from").is("local"));
        log.info("queryObject:{}", query.getQueryObject());
        GridFSFindIterable gridFSFiles = gridFsTemplate.find(query);
        for (GridFSFile gridFSFile : gridFSFiles) {
            log.info("查找到文件信息：{}", gridFSFile.getFilename());
        }

        log.info("GridFSBuckets====================================================");

        GridFSBucket gridFSBucket = GridFSBuckets.create(mongoTemplate.getMongoDatabaseFactory().getMongoDatabase(), "customBucketName");
        ObjectId fileId = gridFSBucket.uploadFromStream(file.getName(), new FileInputStream(file), options);
        gridFSBucket.downloadToStream(fileId, new FileOutputStream(new File(filePath + File.separator + UUID.randomUUID().toString() + "_GridFSBucket_" + resource.getFilename())));

        for (GridFSFile gridFSFile : gridFSBucket.find()) {
            log.info("全量查找==》查找到文件信息：{}", gridFSFile.getFilename());
        }

        Bson bson = Filters.eq("metadata.from", "local");
        GridFSFindIterable gridFSFilesList = gridFSBucket.find(bson);
        for (GridFSFile gridFSFile : gridFSFilesList) {
            log.info("find(bson)查找==》查找到文件信息：{}", gridFSFile.getFilename());
        }
    }
}
